package org.beyondhc.lessons.observer;

import java.util.logging.Logger;

import junit.framework.TestCase;

import org.beyondhc.lessons.observer.genericwithtwoargs.Observable;
import org.beyondhc.lessons.observer.genericwithtwoargs.Observer;
import org.beyondhc.test.LogMsgCaptor;
import org.junit.Test;

/**
 * 
 * Shows how to (badly) use Java Observer-Observable.
 * 
 * @author Jaana Kulmala
 * 
 */
public class GenericObserver extends TestCase {

    public class Koala extends Observable<Koala, Boolean> {

        public void setHungry(final boolean isHungry) {
            setChanged();
            notifyObservers(isHungry);
        }

        @Override
        public String toString() {
            return "koala";
        }
    }

    public class Tiger extends Observable<Tiger, Boolean> {

        public void setHungry(final boolean isHungry) {
            setChanged();
            notifyObservers(isHungry);
        }

        @Override
        public String toString() {
            return "tiger";
        }
    }

    /**
     * 
     * If you implement the interface, you can only observe one type of item.
     * 
     */
    public class ZooKeeper implements Observer<Koala, Boolean> {

        @Override
        public void update(final Koala koala, final Boolean arg) {
            if (Boolean.TRUE.equals(arg)) {
                LOGGER.info("Oh, the " + koala
                        + " is hungry. I'll go into the cage and "
                        + "give some eucalyptus to the little fella!");
            }
        }
    }

    public interface KoalaObserver extends Observer<Koala, Boolean> {

    }

    public class MoreImprovedKeeper implements KoalaObserver {

        @Override
        public void update(final Koala observable, final Boolean arg) {
            // TODO Auto-generated method stub

        }

    }

    public class ImprovedZooKeeper {

        private final Observer<Koala, Boolean> koalaObserver = new Observer<Koala, Boolean>() {

            @Override
            public void update(final Koala observable, final Boolean arg) {
                if (Boolean.TRUE.equals(arg)) {
                    koalaIsHungry(observable);
                }
            }
        };

        private final Observer<Tiger, Boolean> tigerObserver = new Observer<Tiger, Boolean>() {

            @Override
            public void update(final Tiger observable, final Boolean arg) {
                if (Boolean.TRUE.equals(arg)) {
                    tigerIsHungry(observable);
                }
            }
        };

        public void observeKoala(final Koala koala) {
            koala.addObserver(koalaObserver);
        }

        public void observeTiger(final Tiger tiger) {
            tiger.addObserver(tigerObserver);
        }

        protected void koalaIsHungry(final Koala koala) {
            LOGGER.info("Oh, the " + koala
                    + " is hungry. It needs some eucalyptus.");
        }

        protected void tigerIsHungry(final Tiger tiger) {
            LOGGER.info("Oh, the " + tiger + " is hungry. It needs some meat.");
        }

    }

    private static final transient Logger LOGGER = Logger
            .getLogger(GenericObserver.class.getName());

    @Test
    public void testKoalaGetsFood2() {
        final LogMsgCaptor logMsgCaptor = new LogMsgCaptor(LOGGER);
        final Koala koala = new Koala();
        final ImprovedZooKeeper zooKeeper = new ImprovedZooKeeper();

        zooKeeper.observeKoala(koala);
        koala.setHungry(true);
        assertEquals("The zookeeper should be notified",
                "Oh, the koala is hungry. It needs some eucalyptus.",
                logMsgCaptor.getMsg());

    }

    @Test
    public void testKoalaGetsFood() {
        final LogMsgCaptor logMsgCaptor = new LogMsgCaptor(LOGGER);
        final Koala koala = new Koala();
        final ZooKeeper zooKeeper = new ZooKeeper();

        koala.addObserver(zooKeeper);
        koala.setHungry(true);
        assertEquals("The zookeeper should be notified",
                "Oh, the koala is hungry. I'll go into the cage and "
                        + "give some eucalyptus to the little fella!",
                logMsgCaptor.getMsg());
    }

    @Test
    public void testTiggerGetsFood() {
        final LogMsgCaptor logMsgCaptor = new LogMsgCaptor(LOGGER);
        final Tiger tigger = new Tiger();
        final ImprovedZooKeeper zooKeeper = new ImprovedZooKeeper();

        zooKeeper.observeTiger(tigger);
        tigger.setHungry(true);
        assertEquals("The zookeeper should be notified",
                "Oh, the tiger is hungry. It needs some meat.",
                logMsgCaptor.getMsg());
    };

}
